package com.hanson.aop.interceptor;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.http.ContentType;
import com.alibaba.fastjson.JSONObject;
import com.hanson.common.api.CommonResult;
import com.hanson.common.api.ResultCode;
import com.hanson.common.constant.Constant;
import com.hanson.pojo.vo.TbIpList;
import com.hanson.pojo.vo.TbIpVisitLog;
import com.hanson.service.ITbIpListLogService;
import com.hanson.service.ITbIpListService;
import com.hanson.service.ITbIpVisitLogService;
import com.hanson.utils.IpUtils;
import com.hanson.utils.RedisUtil;
import com.hanson.utils.ThreadPoolUtil;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @description: ip拦截器
 * @classname: IpInterceptor
 * @author: HansonLee
 * @create: 2022/08/31
 **/
@Slf4j
public class IpInterceptor implements HandlerInterceptor {

    private final static String KEY_IP = "user:ip";
    @Autowired
    ITbIpListService ipListService;
    @Autowired
    ITbIpListLogService ipListLogService;
    @Autowired
    ITbIpVisitLogService visitLogService;
    @Autowired
    RedisUtil redisUtil;
    //内网ip区间缓存
    private final static List<Long> intranetIpSection = new ArrayList<>();
    static {
        final String[] sectionStr = {
                "0.0.0.0","0.255.255.255",
                "10.0.0.0","10.255.255.255",
                "100.64.0.0","100.122.255.255",
                "100.123.0.0","100.127.255.255",
                "127.0.0.0","127.255.255.255",
                "169.254.0.0","169.254.255.255",
                "172.16.0.0","172.31.255.255",
                "192.0.0.0","192.0.0.255",
                "192.0.2.0","192.0.2.255",
                "192.88.99.0","192.88.99.255",
                "192.168.0.0","192.168.255.255",
                "198.18.0.0","198.19.255.255",
                "198.51.100.0","198.51.100.255",
                "203.0.113.0","203.0.113.255",
                "224.0.0.0","255.255.255.255"};
        for (String s : sectionStr) {
            intranetIpSection.add(Util.ip2long(s));
        }
    }


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //对静态资源请求默认不作为浏览请求，不进入后面逻辑
        if (handler instanceof ResourceHttpRequestHandler){
            return true;
        }
        if (!(handler instanceof HandlerMethod)){
            return true;
        }
        try {
            //过滤内网ip攻击访问
            String ip = IpUtils.getEffectiveIp(IpUtils.getIpAddr(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()));
            long ip2Long = Util.ip2long(ip);
            int size = intranetIpSection.size();
            //白名单放行
            TbIpList ipList = new TbIpList();
            ipList.setIp(ip).setStatus(Constant.DELETE_FLAG_CLOSE).setType(Constant.WHITE_LIST_IP);
            Integer whiteIp = ipListService.selectCount(ipList);
            if (whiteIp != null && whiteIp > 0) return true;
            for (int i = 0; i < size; i += 2) {
                if (ip2Long >= intranetIpSection.get(i) && ip2Long <= intranetIpSection.get(i + 1)){
                    //内网ip
                    log.info("内网ip访问攻击：{}，ip2long：{},time：{}",ip,ip2Long,new SimpleDateFormat(Constant.Y_M_D_H_M_S).format(new Date()));
                    CommonResult<Object> result = new CommonResult<>(ResultCode.INVALID_IP,"哎呀，你干嘛~");
                    response.setCharacterEncoding("UTF-8");
                    response.setContentType(ContentType.JSON.getValue());
                    response.getWriter().write(JSONObject.toJSONString(result));
                    return false;
                }
            }
            //查询黑名单
            ipList.setType(Constant.BLACK_LIST_IP);
            Integer blackIp = ipListService.selectCount(ipList);
            if (blackIp > 0){
                //拦截黑名单
                CommonResult<Object> result = new CommonResult<>(ResultCode.BLACK_LIST,null);
                response.setCharacterEncoding("UTF-8");
                response.setContentType(ContentType.JSON.getValue());
                response.getWriter().write(JSONObject.toJSONString(result));
                return false;
            }
            //记录ip浏览记录
            Integer visitDate = Integer.valueOf(DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATE_PATTERN));
            TbIpVisitLog visitLog = new TbIpVisitLog();
            visitLog.setIp(ip).setVisitDate(visitDate).setStatus(Constant.DELETE_FLAG_CLOSE);
            Integer existLog = visitLogService.selectCount(visitLog);
            if (existLog <= 0){
                //缓存解析
                long ip2long = Util.ip2long(ip);
                Object cacheIp = redisUtil.hGet(KEY_IP, String.valueOf(ip2long));
                if (cacheIp != null){
                    visitLog.setIpAddress(cacheIp.toString());
                }else {
                    //离线解析
                    String ipOffLine = IpUtils.getIpOffLine(ip);
                    visitLog.setIpAddress(ipOffLine);
                    //异步回写缓存
                    ThreadPoolUtil.getPool().execute(()-> redisUtil.hPut(KEY_IP,String.valueOf(ip2long),ipOffLine));
                    //设置过期时间，24小时
                    Long expire = redisUtil.getExpire(KEY_IP);
                    if (expire <= 0){
                        synchronized (this){
                            redisUtil.expire(KEY_IP,24L, TimeUnit.HOURS);
                        }
                    }
                }
                visitLog.setCreateBy(Constant.ADMIN_USER);
                visitLogService.add(visitLog);
            }else {
                visitLogService.update(visitLog);
            }
        } catch (Exception e) {
            //nothing
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}
